
local alien = require 'alien'
local bit = require 'bit'
local print
    = print
local bor, lshift
    = bit.bor, bit.lshift


module(...)


local lib = alien.load('gdiplus')

-- constants/enums
FontStyleRegular = 0
FontStyleBold = 1
FontStyleItalic = 2
FontStyleBoldItalic = 3
FontStyleUnderline = 4
FontStyleStrikeout = 8

UnitWorld = 0
UnitDisplay = 1
UnitPixel = 2
UnitPoint = 3
UnitInch = 4
UnitDocument = 5
UnitMillimeter = 6

TextRenderingHintSystemDefault = 0
TextRenderingHintSingleBitPerPixelGridFit = 1
TextRenderingHintSingleBitPerPixel = 2
TextRenderingHintAntiAliasGridFit = 3
TextRenderingHintAntiAlias = 4
TextRenderingHintClearTypeGridFit = 5

Ok = 0

DebugEventLevelFatal = 0
DebugEventLevelWarning = 1

PixelFormatIndexed = 0x00010000
PixelFormatGDI = 0x00020000
PixelFormatAlpha = 0x00040000
PixelFormatPAlpha = 0x00080000
PixelFormatExtended = 0x00100000
PixelFormatCanonical = 0x00200000 

PixelFormatUndefined = 0
PixelFormatDontCare = 0

PixelFormat1bppIndexed = bor(1, lshift(1, 8), PixelFormatIndexed, PixelFormatGDI)
PixelFormat4bppIndexed = bor(2, lshift(4, 8), PixelFormatIndexed, PixelFormatGDI)
PixelFormat8bppIndexed = bor(3, lshift(8, 8), PixelFormatIndexed, PixelFormatGDI)
PixelFormat16bppGrayScale = bor(4, lshift(16, 8), PixelFormatExtended)
PixelFormat16bppRGB555 = bor(5, lshift(16, 8), PixelFormatGDI)
PixelFormat16bppRGB565 = bor(6, lshift(16, 8), PixelFormatGDI)
PixelFormat16bppARGB1555 = bor(7, lshift(16, 8), PixelFormatAlpha, PixelFormatGDI)
PixelFormat24bppRGB = bor(8, lshift(24, 8), PixelFormatGDI)
PixelFormat32bppRGB = bor(9, lshift(32, 8), PixelFormatGDI)
PixelFormat32bppARGB = bor(10, lshift(32, 8), PixelFormatAlpha, PixelFormatGDI, PixelFormatCanonical)
PixelFormat32bppPARGB = bor(11, lshift(32, 8), PixelFormatAlpha, PixelFormatPAlpha, PixelFormatGDI)
PixelFormat48bppRGB = bor(12, lshift(48, 8), PixelFormatExtended)
PixelFormat64bppARGB = bor(13, lshift(64, 8), PixelFormatAlpha, PixelFormatCanonical, PixelFormatExtended)
PixelFormat64bppPARGB = bor(14, lshift(64, 8), PixelFormatAlpha, PixelFormatPAlpha, PixelFormatExtended)
PixelFormatMax = 15

StringAlignmentNear   = 0
StringAlignmentCenter = 1
StringAlignmentFar    = 2

ImageLockModeRead        = 0x0001
ImageLockModeWrite       = 0x0002
ImageLockModeUserInputBuf= 0x0004

StringFormatFlagsDirectionRightToLeft = 0x00000001
StringFormatFlagsDirectionVertical = 0x00000002
StringFormatFlagsNoFitBlackBox = 0x00000004
StringFormatFlagsDisplayFormatControl = 0x00000020
StringFormatFlagsNoFontFallback = 0x00000400
StringFormatFlagsMeasureTrailingSpaces = 0x00000800
StringFormatFlagsNoWrap = 0x00001000
StringFormatFlagsLineLimit = 0x00002000
StringFormatFlagsNoClip = 0x00004000

StringTrimmingNone = 0
StringTrimmingCharacter = 1
StringTrimmingWord = 2
StringTrimmingEllipsisCharacter = 3
StringTrimmingEllipsisWord = 4
StringTrimmingEllipsisPath = 5

-- structs
StartupInput = alien.defstruct{
  {"GdiplusVersion", "uint"};
  {"DebugEventCallback", "callback"};
  {"SuppressBackgroundThread", "int" --[[BOOL]]};
  {"SuppressExternalCodecs", "int" --[[BOOL]]};
}

StartupOutput = alien.defstruct{
  {"NotificationHook", "callback"};
  {"NotificationUnhook", "callback"};
}

BitmapData = alien.defstruct{
  {"width", "uint"};
  {"height", "uint"};
  {"Stride", "int"};
  {"PixelFormat", "int"};
  {"Scan0", "pointer"};
  {"Reserved", "pointer"};
}

LOGFONTA = alien.defstruct{
  {"lfHeight", "long"};
  {"lfWidth", "long"};
  {"lfEscapement", "long"};
  {"lfOrientation", "long"};
  {"lfItalic", "byte"};
  {"lfUnderline", "byte"};
  {"lfStrikeout", "byte"};
  {"lfCharSet", "byte"};
  {"lfOutPrecision", "byte"};
  {"lfClipPrecision", "byte"};
  {"lfQuality", "byte"};
  {"lfPitchAndFamily", "byte"};
  -- CHAR lfFaceName[LF_FACESIZE];
}
LOGFONTA.size = LOGFONTA.size + 32

RectF = alien.defstruct{
  {"x", "float"};
  {"y", "float"};
  {"width", "float"};
  {"height", "float"};
}

Rect = alien.defstruct{
  {"X", "int"};
  {"Y", "int"};
  {"Width", "int"};
  {"Height", "int"};
}

-- callback signatures
DebugEventProc_types = { ret="void"; "int" --[[DebugEventLevel]], "string" }
NotificationHookProc_types = { ret="int" --[[Status]]; }

-- startup
Startup = lib.GdiplusStartup
Startup:types{ ret="int"; "ref uint" --[[token]], "pointer" --[[GdiplusStartupInput*]], "pointer" --[[GdiplusStartupOutput*]] }

local function Func(name, types)
  types.ret = types.ret or "int" --[[GpStatus]]
  print(name)
  local func = lib["Gdip" .. name]
  func:types(types)
  _M[name] = func
end

Func("CreateBitmapFromFile", { "pointer" --[[GDIPCONST WCHAR*]], "pointer" --[[GpBitmap**]] })
Func("BitmapLockBits", {
  "pointer" --[[GpBitmap*]], "pointer" --[[GDIPCONST GpRect*]], "uint" --[[flags]], "int" --[[PixelFormat]], "pointer" --[[BitmapData*]] })
Func("BitmapUnlockBits", { "pointer" --[[GpBitmap*]], "pointer" --[[BitmapData*]] })
Func("CreateBitmapFromScan0", {
  "int" --[[width]], "int" --[[height]], "int" --[[stride]], "int" --[[PixelFormat]], "pointer" --[[BYTE* scan0]], "pointer" --[[GpBitmap**]] })
Func("NewPrivateFontCollection", { "pointer" --[[GpFontCollection**]] })
Func("PrivateAddFontFile", { "pointer" --[[GpFontCollection*]], "pointer" --[[GDIPCONST WCHAR* filename]] })
Func("GetFontCollectionFamilyCount", { "pointer" --[[GpFontCollection*]], "ref int" --[[numFound]] })
Func("GetFontCollectionFamilyList", {"pointer" --[[GpFontCollection*]], "int" --[[numSought]], "pointer" --[[GpFontFamily**]], "ref int" --[[numFound]] })
Func("CreateFont", { "pointer" --[[GpFontFamily*]], "float" --[[emSize]], "int" --[[style]], "int" --[[unit]], "pointer" --[[GpFont**]] })
Func("MeasureString", {
  "pointer" --[[GpGraphics*]], "pointer" --[[GDIPCONST WCHAR*]], "int" --[[length]], "pointer" --[[GDIPCONST GpFont*]],
  "pointer" --[[GDIPCONST RectF* layoutRect]], "pointer" --[[GDIPCONST GpStringFormat*]], "pointer" --[[RectF *boundingBox]],
  "ref int" --[[codepointsFitted]], "ref int" --[[linesFilled]] })
Func("DrawString", {
  "pointer" --[[GpGraphics*]], "pointer" --[[GDIPCONST WCHAR*]], "int" --[[length]], "pointer" --[[GDIPCONST GpFont*]],
  "pointer" --[[GDIPCONST RectF* layoutRect]], "pointer" --[[GDIPCONST GpStringFormat*]], "pointer" --[[GDIPCONST GpBrush*]] })
Func("GetLogFontA", { "pointer" --[[GpFont*]], "pointer" --[[GpGraphics*]], "pointer" --[[LOGFONTA*]] })
Func("GetImageGraphicsContext", { "pointer" --[[GpImage*]], "pointer" --[[GpGraphics**]] })
Func("CreateSolidFill", { "uint" --[[ARGB]], "pointer" --[[GpSolidFill**]] })
Func("GraphicsClear", { "pointer" --[[GpGraphics*]], "uint" --[[ARGB]] })
Func("DeleteGraphics", { "pointer" --[[GpGraphics*]] })
Func("CreateStringFormat", { "int" --[[formatAttributes]], "ushort" --[[langid]], "pointer" --[[GpStringFormat**]] })
Func("CloneStringFormat", { "pointer" --[[GDIPCONST GpStringFormat*]], "pointer" --[[GpStringFormat**]] })
Func("DeleteStringFormat", { "pointer" --[[GpStringFormat*]] })

local function StringFormatProperty(name, t, readonly)
  Func("GetStringFormat" .. name, { "pointer" --[[GDIPCONST GpStringFormat*]], "ref " .. t })
  if not readonly then
    Func("SetStringFormat" .. name, { "pointer" --[[GpStringFormat*]], t })
  end
end

StringFormatProperty("Align", "uint" --[[StringAlignment]])
Func("GetStringFormatDigitSubstitution", { "pointer" --[[GDIPCONST GpStringFormat*]], "pointer" --[[LANGID*]], "ref uint" --[[StringDigitSubtitute]] })
StringFormatProperty("Flags", "int")
StringFormatProperty("Trimming", "int")
StringFormatProperty("HotkeyPrefix", "int")
StringFormatProperty("LineAlign", "uint" --[[StringAlignment]])
StringFormatProperty("MeasurableCharacterRangeCount", "int", true)
StringFormatProperty("TabStopCount", "int", true)
Func("GetStringFormatTabStops", {
  "pointer" --[[GDIPCONST GpStringFormat*]], "int" --[[count]], "pointer" --[[float* firstTabOffset]], "pointer" --[[float* tabStops]] })

Func("GetImageWidth", { "pointer" --[[GpImage*]], "ref uint" })
Func("GetImageHeight", { "pointer" --[[GpImage*]], "ref uint" })
Func("GetImagePixelFormat", { "pointer" --[[GpImage*]], "ref uint" --[[PixelFormat*]] })
Func("GetImageHorizontalResolution", { "pointer" --[[GpImage*]], "pointer" --[[float*]] })
Func("GetImageVerticalResolution", { "pointer" --[[GpImage*]], "pointer" --[[float*]] })

Func("BitmapSetResolution", { "pointer" --[[GpBitmap*]], "float" --[[xdpi]], "float" --[[ydpi]] })

Func("SetTextRenderingHint", { "pointer" --[[GpGraphics*]], "int" --[[TextRenderingHint]] })
Func("GetTextRenderingHint", { "pointer" --[[GpGraphics*]], "ref int" --[[TextRenderingHint*]] })

Func("DrawRectangle", { "pointer" --[[GpGraphics*]], "pointer" --[[GpPen*]], "float" --[[x]], "float" --[[y]], "float" --[[width]], "float" --[[height]] })

Func("FillRectangleI", { "pointer" --[[GpGraphics*]], "pointer" --[[GpBrush*]], "int" --[[x]], "int" --[[y]], "int" --[[width]], "int" --[[height]] })

Func("StringFormatGetGenericTypographic", { "pointer" --[[GpStringFormat**]] })
